#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sem.h> // 信号量
#include <sys/ipc.h> // IPC
#include <signal.h>

typedef union semun
{
  int val;
  struct semid_ds *buf;
  unsigned short *array;
} semun;

void init_ops(struct sembuf *ops)
{
  ops[0].sem_num = 0;
  ops[0].sem_op = -1;
  ops[0].sem_flg = SEM_UNDO;

  ops[1].sem_num = 1;
  ops[1].sem_op = -1;
  ops[1].sem_flg = SEM_UNDO;

  ops[2].sem_num = 2;
  ops[2].sem_op = -1;
  ops[2].sem_flg = SEM_UNDO;

  ops[3].sem_num = 0;
  ops[3].sem_op = 1;
  ops[3].sem_flg = SEM_UNDO;

  ops[4].sem_num = 1;
  ops[4].sem_op = 1;
  ops[4].sem_flg = SEM_UNDO;

  ops[5].sem_num = 2;
  ops[5].sem_op = 1;
  ops[5].sem_flg = SEM_UNDO;
}

int main()
{

  key_t key;
  int semid;
  pid_t pid_1, pid_2, pid_3, pid_4;

  // 获取key
  key = ftok("/", 'b');
  if (key < 0)
  {
    perror("获取key失败");
    exit(1);
  }
  // 获取信号量id
  semid = semget(key, 3, 0666 | IPC_CREAT);
  if (semid < 0)
  {
    perror("获取信号量失败");
    exit(1);
  }

  // 初始化信号量
  semun arg;
  unsigned short arr[] = {0, 0, 3}; // 信号量值(苹果,桔子,果盘空间)
  arg.array = arr;
  semctl(semid, 0, SETALL, arg); // 设置信号量

  // 初始化信号量操作
  struct sembuf ops[6];
  init_ops(ops);

  // 创建3个子进程
  pid_1 = fork();
  if (pid_1 > 0)
  {
    pid_2 = fork();
    if (pid_2 > 0)
    {
      pid_3 = fork();
      if (pid_3 > 0)
      {
        pid_4 = fork();
        if (pid_4 > 0)
        {
          printf("输入任意键退出\n");
          read(0, NULL, 20);
          // 关闭子进程
          kill(pid_1, 9);
          kill(pid_2, 9);
          kill(pid_4, 9);
          kill(pid_3, 9);
          // 删除信号量
          semctl(semid, 0, IPC_RMID);
          printf("结束\n");
          exit(0);
        }
        else if (pid_4 == 0)
        {
          // 女儿,只吃桔子
          while (1)
          {
            // 等待桔子
            semop(semid, &ops[1], 1);
            // 释放果盘空间
            semop(semid, &ops[5], 1);
            // 模拟吃桔子时间
            sleep(5);
            // 吃桔子
            printf("女儿吃了一个桔子\n");
          }
        }
        else
        {
          perror("创建进程失败");
          // 向子进程发送结束信号
          kill(pid_1, 9);
          kill(pid_2, 9);
          kill(pid_3, 9);
          exit(1);
        }
      }
      else if (pid_3 == 0)
      {
        // 儿子,只吃苹果
        while (1)
        {
          // 等待苹果
          semop(semid, &ops[0], 1);
          // 释放果盘空间
          semop(semid, &ops[5], 1);
          // 模拟吃苹果时间
          sleep(5);
          // 吃苹果
          printf("儿子吃了一个苹果\n");
        }
      }
      else
      {
        perror("创建进程失败");
        // 向子进程发送结束信号
        kill(pid_2, 9);
        kill(pid_1, 9);
        exit(1);
      }
    }
    else if (pid_2 == 0)
    {
      // 母亲,只产生桔子
      while (1)
      {
        // 等待空间
        semop(semid, &ops[2], 1);
        // 模拟生产时间
        sleep(5);
        // 生产桔子
        printf("母亲剥了一个桔子\n");
        semop(semid, &ops[4], 1);
      }
    }
    else
    {
      perror("创建进程失败");
      // 向子进程发送结束信号
      kill(pid_1, 9);
      exit(1);
    }
  }
  else if (pid_1 == 0)
  {
    // 父亲,只产生苹果
    while (1)
    {
      // 等待空间
      semop(semid, &ops[2], 1);
      // 模拟生产时间
      sleep(5);
      // 生产苹果
      printf("父亲削了一个苹果\n");
      semop(semid, &ops[3], 1);
    }
  }
  else
  {
    perror("创建进程失败");
    exit(1);
  }
}